/*
 * Funny Language - a free style programming language.
 * Copyright (C) 2015 by fanguangping (fanguangping@163.com)
 * vec.c
 */

#include "../basic/operator.h"


static int count_consecutive_cells(Cell* x, int needed) {
	int n = 1;
	//如果是连续的cell ， cdr(x)与x+1 必然相等
	while (cdr(x) == x + 1) {
		x = cdr(x);
		n++;
		if (n > needed)
			return n;
	}
	return n;
}

static Cell* find_consecutive_cells(Scheme *sc, int n) {
	Cell* *pp = &sc->freeCell;
	while (*pp != &g_nil) {
		int count = count_consecutive_cells(*pp, n);
		if (count >= n) {
			Cell* x = *pp;
			*pp = (*pp + n - 1)->_pair._cdr;
			sc->freeCellCount -= n;
			return x;
		}
		pp = &((*pp + count - 1)->_pair._cdr);
	}
	return &g_nil;
}

//获取n个连续的cell
static Cell* get_consecutive_cells(Scheme *sc, int n) {
	/* Are there any cells available? */
	Cell* x = find_consecutive_cells(sc, n);
	if (x != &g_nil)
		return x;

	/* If not, try gc'ing some */
	gc(sc, &g_nil, &g_nil);
	x = find_consecutive_cells(sc, n);
	if (x != &g_nil)
		return x;

	/* If there still aren't, try getting more heap */
	if (alloc_cellseg(sc, 1)) {
		x = find_consecutive_cells(sc, n);
		if (x != &g_nil)
			return x;
	}

	printf("No Memory ! ! !\n");
	while (TRUE)
		getchar();
	return &g_nil;
}

//创建数组
Cell* make_vector(Scheme *sc, int len) {
	//一个cell中的 _cons可以保存两个元素，所以只要len/2+len%2个cell就可以保存len个元素
	// 必能分配成功，失败不会返回
	Cell* cells = get_consecutive_cells(sc, len / 2 + len % 2 + 1);
	//cells[0]用于保存数组控制信息
	//(cells)->_flag = T_ATOM;	    //是数组，并是一个原子
	(cells)->_type = VECTOR_TYPE;
	(cells)->_longValue = (long) len;	//设置数组的长度
	fill_vector(cells, &g_nil);
	return cells;
}






//取数组中的第index个元素
Cell* get_vector_item(Cell* vec, int index) {
	int n = index / 2;
	if (index % 2 == 0) {
		return car(vec + 1 + n);
	} else {
		return cdr(vec + 1 + n);
	}
}

//设置数组中的第index个元素的值
Cell* set_vector_item(Cell* vec, int index, Cell* a) {
	int n = index / 2;
	Cell* p = vec + 1 + n;
	if (index % 2 == 0) {
		p->_pair._car = a;
		return (p->_pair._car);
	} else {
		p->_pair._cdr = a;
		return (p->_pair._cdr);
	}
}

//填充数组
void fill_vector(Cell* vec, Cell* fill_obj) {
	int num = vec->_longValue / 2 + vec->_longValue % 2;
	Cell* p = NULL;
	int i;
	for (i = 0; i < num; i++) {
		p = vec + 1 + i;
		//p->_flag = T_PAIR;
		p->_type = PAIR_TYPE;
		set_immutable(p);		//不可变的
		p->_pair._car = fill_obj;		//填充数组的内容
		p->_pair._cdr = fill_obj;		//填充数组的内容
	}
}

// (vector x y ...)
Cell* op_vector(Scheme *sc) {
	Cell* x;
	int len = ls_length(sc, sc->args);
	if (len < 0)
		return make_default_exception_1(sc, "NOT_A_PROPER_LIST", "vector: not a proper list: %s", cell2str(sc, sc->args));
	Cell* vec = make_vector(sc, len);
	int index = 0;
	for (x = sc->args; type_of(sc, x, PAIR_TYPE); x = cdr(x), index++) {
		set_vector_item(vec, index, car(x));
	}
	sc->returnValue = vec;
	return pop_callstack(sc);
}

// (make-vector n e)
Cell* op_makevector(Scheme *sc) {
	int len = first(sc->args)->_longValue;
	Cell* vec = make_vector(sc, len);
	if (rest(sc->args) != &g_nil) {
		fill_vector(vec, second(sc->args));
	}
	sc->returnValue = vec;
	return pop_callstack(sc);
}

// (vector-length x)
Cell* op_veclen(Scheme *sc) {
	sc->returnValue = make_integer(sc, first(sc->args)->_longValue);
	return pop_callstack(sc);
}

// (vector-ref x index)
Cell* op_vecref(Scheme *sc) {
	int index = second(sc->args)->_longValue;
	if (index < 0 || index >= first(sc->args)->_longValue) {
		return make_default_exception_1(sc, "OUT_OF_BOUNDS", "vector-ref: out of bounds:%s", cell2str(sc, second(sc->args)));
	}
	sc->returnValue = get_vector_item(first(sc->args), index);
	return pop_callstack(sc);
}

// (vector-set! x index e)
Cell* op_vecset(Scheme *sc) {
	if (is_immutable(first(sc->args))) {
		return make_default_exception_1(sc, "UNABLE_TO_ALTER", "vector-set!: unable to alter immutable vector:%s", cell2str(sc, first(sc->args)));
	}
	int index = second(sc->args)->_longValue;
	if (index < 0 || index >= first(sc->args)->_longValue) {
		return make_default_exception_1(sc, "OUT_OF_BOUNDS", "vector-set!: out of bounds:%s", cell2str(sc, second(sc->args)));
	}
	set_vector_item(first(sc->args), index, third(sc->args));
	sc->returnValue = first(sc->args);
	return pop_callstack(sc);
}

void init_vector_operators(Scheme *sc) {
	ADD_OPERATOR(sc, "vector", vector);
	ADD_OPERATOR(sc, "make-vector", makevector);
	ADD_OPERATOR(sc, "vector-length", veclen);
	ADD_OPERATOR(sc, "vector-ref", vecref);
	ADD_OPERATOR(sc, "vector-set!", vecset);
}

